#include "flash.h"
#include "boot.h"
#include "variable.h"

/* 变量 ----------------------------------------------------------------------*/
volatile FLASH_Status FLASHStatus = FLASH_COMPLETE;

/************************************************
函数名称 ： FLASH_PageWrite
功    能 ： 写一页FLASH
参    数 ： pBuffer ----- 数据缓冲区
            WriteAddr --- 写地址
返 回 值 ： 无
作    者 ： 
*************************************************/
void FLASH_PageWrite(uint16_t *pBuffer, uint32_t WriteAddr)
{
  uint16_t cnt_tmp;
  for(cnt_tmp=0; (cnt_tmp<FLASH_PAGE_LENGTH) && (FLASHStatus == FLASH_COMPLETE); cnt_tmp++)
  {
    FLASHStatus = FLASH_ProgramHalfWord(WriteAddr, *pBuffer);
    WriteAddr += 2;
    pBuffer++;
  }
}

/************************************************
函数名称 ： FLASH_WriteNWord
功    能 ： 写Flash
参    数 ： pBuffer ----- 数据缓冲区
            WriteAddr --- 写地址
            nWord ------- 长度
返 回 值 ： 无
作    者 ： 
*************************************************/
void FLASH_WriteNWord(uint16_t *pBuffer, uint32_t WriteAddr, uint16_t nWord)
{
  static uint16_t buf_tmp[FLASH_PAGE_LENGTH];
  uint32_t NbrOfPage = 0x00;                     //页数（读写）

  uint32_t length_beyond_start;                  //开始页超出的长度(半字)
  uint32_t length_remain_start;                  //开始页剩余的长度(半字)

  uint32_t addr_first_page;                      //第一页(起始)地址
  uint32_t addr_last_page;                       //最后页(起始)地址
  uint16_t *pbuf_tmp;                            //buf指针
  uint16_t cnt_length;                           //计数 - 长度
  uint16_t cnt_page;                             //计数 - 页数
  uint32_t prog_addr_start;                      //编程地址
  uint32_t length_beyond_last;                   //最后页超出的长度(半字)
  uint8_t  flag_last_page_fill;                  //最后一页装满标志


  length_beyond_start = ((WriteAddr % FLASH_PAGE_SIZE) / FLASH_TYPE_LENGTH);
  length_remain_start = FLASH_PAGE_LENGTH - length_beyond_start;
  addr_first_page     = WriteAddr - (WriteAddr % FLASH_PAGE_SIZE);

  /* 擦除(写操作)的页数 */
  if(0 == (length_beyond_start + nWord)%FLASH_PAGE_LENGTH)
  {
    flag_last_page_fill = FLAG_OK;               //最后一页刚好
    NbrOfPage = (length_beyond_start + nWord) / FLASH_PAGE_LENGTH;
  }
  else
  {
    flag_last_page_fill = FLAG_NOOK;             //···跨页
    NbrOfPage = (length_beyond_start + nWord) / FLASH_PAGE_LENGTH + 1;
  }

  /* 解锁 */
  FLASH_Unlock();

  /* 清除标志位 */
  FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR);

  /* 操作第一页 */
  FLASH_ReadNWord(buf_tmp, addr_first_page, FLASH_PAGE_LENGTH);
  FLASHStatus = FLASH_ErasePage(addr_first_page);
  /* 只有1页 */
  if(1 == NbrOfPage)
  {
    pbuf_tmp = pBuffer;                          //复制地址(指针)
    for(cnt_length=length_beyond_start; cnt_length<(length_beyond_start + nWord); cnt_length++)
    {
      buf_tmp[cnt_length] = *pbuf_tmp;
      pbuf_tmp++;
    }
    FLASH_PageWrite(buf_tmp, addr_first_page);
  }
  /* 大于1页 */
  else
  {
    /* 第一页 */
    pbuf_tmp = pBuffer;
    for(cnt_length=length_beyond_start; cnt_length<FLASH_PAGE_LENGTH; cnt_length++)
    {
      buf_tmp[cnt_length] = *pbuf_tmp;
      pbuf_tmp++;
    }
    FLASH_PageWrite(buf_tmp, addr_first_page);

    /* 最后一页刚好装满，不用读取最后一页数据 */
    if(FLAG_OK == flag_last_page_fill)
    {
      for(cnt_page=1; (cnt_page<NbrOfPage)  && (FLASHStatus == FLASH_COMPLETE); cnt_page++)
      {                                          //这里编程地址为字节地址(故FLASH_PAGE_SIZE)
        prog_addr_start = addr_first_page + cnt_page*FLASH_PAGE_SIZE;
        FLASHStatus = FLASH_ErasePage(prog_addr_start);
                                                 //(cnt_page - 1):为下一页地址
        FLASH_PageWrite((pBuffer + length_remain_start + (cnt_page - 1)*FLASH_PAGE_LENGTH), prog_addr_start);
      }
    }
    else
    {
      /* 中间页 */
      for(cnt_page=1; (cnt_page<(NbrOfPage - 1))  && (FLASHStatus == FLASH_COMPLETE); cnt_page++)
      {                                          //这里编程地址为字节地址(故FLASH_PAGE_SIZE)
        prog_addr_start = addr_first_page + cnt_page*FLASH_PAGE_SIZE;
        FLASHStatus = FLASH_ErasePage(prog_addr_start);
                                                 //(cnt_page - 1):为下一页地址
        FLASH_PageWrite((pBuffer + length_remain_start + (cnt_page - 1)*FLASH_PAGE_LENGTH), prog_addr_start);
      }

      /* 最后一页 */
      addr_last_page = addr_first_page + (NbrOfPage - 1)*FLASH_PAGE_SIZE;

      FLASH_ReadNWord(buf_tmp, addr_last_page, FLASH_PAGE_LENGTH);
      FLASHStatus = FLASH_ErasePage(addr_last_page);
                                                 //NbrOfPage - 2: 首页 + 最后一页 共两页(-2)
      pbuf_tmp = pBuffer + length_remain_start + (NbrOfPage - 2)*(FLASH_PAGE_SIZE/2);
      length_beyond_last   = (nWord - length_remain_start) % FLASH_PAGE_LENGTH;
      for(cnt_length=0; cnt_length<length_beyond_last; cnt_length++)
      {
        buf_tmp[cnt_length] = *pbuf_tmp;
        pbuf_tmp++;
      }
      FLASH_PageWrite(buf_tmp, addr_last_page);
    }
  }
}

/************************************************
函数名称 ： FLASH_ReadNWord
功    能 ： 读N字
参    数 ： pBuffer ----- 数据缓冲区
            ReadAddr ---- 读地址
            nWord ------- 长度
返 回 值 ： 无
作    者 ： 
*************************************************/
void FLASH_ReadNWord(uint16_t* pBuffer, uint32_t ReadAddr, uint16_t nWord)
{
  while(nWord--)
  {
    *pBuffer = (*(__IO uint16_t*)ReadAddr);
    ReadAddr += 2;
    pBuffer++;
  }
}

/**
 * 描述：
 *    从指定地址开始擦除指定长度的数据（同时会删除）
 *    特别注意:因为STM32的扇区实在太大,没办法本地保存扇区数据,所以本函数
 *             写地址如果非0XFF,那么会先擦除整个扇区且不保存扇区数据.所以
 *             写非0XFF的地址,将导致整个扇区数据丢失.建议写之前确保扇区里
 *             没有重要数据,最好是整个扇区先擦除了,然后慢慢往后写. 
 * 
 * 参数：
 *    EraseAddr：起始地址(此地址必须为4的倍数!!)
 *    NumToErase：要擦除的页数
 * 
 * 返回值：
 *     0：成功擦除
 *    -1：写入非法地址
 *    -2：擦除失败
 */
int STMFLASH_Erase(uint32_t EraseAddr, uint32_t NumToErase)
{
	int result = 0;
	if(EraseAddr<STM32_FLASH_BASE || EraseAddr%4) 	
	{
		return -1;	// 非法地址
	}
	else
	{
		FLASH_Unlock(); // 解锁
		FLASH_PrefetchBufferCmd(DISABLE);					 // FLASH擦除期间,必须禁止数据缓存
	 /* 清除标志位 */
		FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR);
		// VCC=2.7~3.6V之间!!

		uint32_t endAddr = EraseAddr + FLASH_PAGE_SIZE*NumToErase;
		uint32_t FLASH_Sector = ADDR_FLASH_PAGE_1;
		uint32_t nextEraseAddr = EraseAddr;
		
		if(EraseAddr < 0x1FFFEC00 && EraseAddr >= ADDR_FLASH_PAGE_0 && endAddr < 0x1FFFEC00) // 只有主存储区,才需要执行擦除操作!!
		{
			while(EraseAddr < endAddr) 				// 扫清一切障碍.(对非FFFFFFFF的地方,先擦除)
			{
				if(EraseAddr < ADDR_FLASH_PAGE_1) 		 	 	 { FLASH_Sector = ADDR_FLASH_PAGE_0 ; nextEraseAddr = ADDR_FLASH_PAGE_1 ; }
				else if(EraseAddr < ADDR_FLASH_PAGE_2) 	 	 { FLASH_Sector = ADDR_FLASH_PAGE_1 ; nextEraseAddr = ADDR_FLASH_PAGE_2 ; }
				else if(EraseAddr < ADDR_FLASH_PAGE_3) 	 	 { FLASH_Sector = ADDR_FLASH_PAGE_2 ; nextEraseAddr = ADDR_FLASH_PAGE_3 ; }
				else if(EraseAddr < ADDR_FLASH_PAGE_4) 	 	 { FLASH_Sector = ADDR_FLASH_PAGE_3 ; nextEraseAddr = ADDR_FLASH_PAGE_4 ; }
				else if(EraseAddr < ADDR_FLASH_PAGE_5) 	 	 { FLASH_Sector = ADDR_FLASH_PAGE_4 ; nextEraseAddr = ADDR_FLASH_PAGE_5 ; }
				else if(EraseAddr < ADDR_FLASH_PAGE_6) 	 	 { FLASH_Sector = ADDR_FLASH_PAGE_5 ; nextEraseAddr = ADDR_FLASH_PAGE_6 ; }
				else if(EraseAddr < ADDR_FLASH_PAGE_7) 	 	 { FLASH_Sector = ADDR_FLASH_PAGE_6 ; nextEraseAddr = ADDR_FLASH_PAGE_7 ; }
				else if(EraseAddr < ADDR_FLASH_PAGE_8) 	 	 { FLASH_Sector = ADDR_FLASH_PAGE_7 ; nextEraseAddr = ADDR_FLASH_PAGE_8 ; }
				else if(EraseAddr < ADDR_FLASH_PAGE_9) 	 	 { FLASH_Sector = ADDR_FLASH_PAGE_8 ; nextEraseAddr = ADDR_FLASH_PAGE_9 ; }
				else if(EraseAddr < ADDR_FLASH_PAGE_10) 	 { FLASH_Sector = ADDR_FLASH_PAGE_9 ; nextEraseAddr = ADDR_FLASH_PAGE_10; }
				else if(EraseAddr < ADDR_FLASH_PAGE_11) 	 { FLASH_Sector = ADDR_FLASH_PAGE_10; nextEraseAddr = ADDR_FLASH_PAGE_11; }
				else if(EraseAddr < ADDR_FLASH_PAGE_12) 	 { FLASH_Sector = ADDR_FLASH_PAGE_11; nextEraseAddr = ADDR_FLASH_PAGE_12; }
				else if(EraseAddr < ADDR_FLASH_PAGE_13) 	 { FLASH_Sector = ADDR_FLASH_PAGE_12; nextEraseAddr = ADDR_FLASH_PAGE_13; }
				else if(EraseAddr < ADDR_FLASH_PAGE_14) 	 { FLASH_Sector = ADDR_FLASH_PAGE_13; nextEraseAddr = ADDR_FLASH_PAGE_14; }
				else if(EraseAddr < ADDR_FLASH_PAGE_15) 	 { FLASH_Sector = ADDR_FLASH_PAGE_14; nextEraseAddr = ADDR_FLASH_PAGE_15; }
				else { FLASH_Sector = ADDR_FLASH_PAGE_15; nextEraseAddr = 0x1FFFEC00; }
				
				// VCC=2.7~3.6V之间!!
				if(FLASH_ErasePage(FLASH_Sector) != FLASH_COMPLETE) 			{ result = -2;  break; } // 发生错误了 
				else  																										{ EraseAddr = nextEraseAddr; }
			}
			
			FLASH_PrefetchBufferCmd(ENABLE);	// FLASH擦除结束,开启数据缓存
		
			FLASH_Lock(); // 上锁
		}
		else 
		{
			result = -1;
		}
	}
	return result;
}

// 擦除所有APP程序
void FLASH_EraseApp(void)
{
	STMFLASH_Erase(ADDR_FLASH_PAGE_5, 11);
}

/*
往指定flash地址里面保存数据
*/
void Flash_writeData(uint32_t addr, uint16_t data)
{
	FLASH_WriteNWord(&data, addr,1);
}


uint16_t STMFLASH_ReadWord(uint32_t read_addr)
{
	return *(volatile uint16_t*)read_addr; 
}



uint32_t STMFLASH_ReadInt32(uint32_t read_addr)
{
	return *(volatile uint32_t*)read_addr; 
}

void Flash_writeInt32(uint32_t addr, uint32_t data)
{
	
	uint16_t buf[2];
	buf[1] = (data>>16) & 0x0000ffff;
	buf[0] = data & 0x0000ffff;
	FLASH_WriteNWord(buf, addr,2);
}


void U8toU16(uint16_t *Buf16,char *Buf8,int len)
{
	for(int i=0; i<len; i++)
	{
			Buf16[i] = Buf8[i];
	}
}

// 拆分
void U32toU16(uint32_t *Buf32,uint16_t *Buf16,int len)
{
	int index=0,i;
	for(i=0; i<len; i++)
	{
		Buf16[index]   = (Buf32[i]>>16)&0xffff;
		Buf16[index+1] = Buf32[i]&0xffff;
		index+=2;
	}
}
// 合并
void U16toU32(uint16_t *Buf16,uint32_t *Buf32,int len)
{
	int index=0,i;
	for(i=0; i<len; i+=2)
	{
		Buf32[index] = Buf16[i]<<16 | Buf16[i+1];
		index++;
	}
}


// 两字节合成u6
int U8mergeU16(uint16_t *Buf16,uint8_t *Buf8,int len)
{
	int i;
	int re = len/2;
	if(len%2)  // 单数
	{
		re = re+1;
		for(i=0; i<len-1; i+=2)
		{
				Buf16[i/2] = ((uint16_t)Buf8[i+1]<<8) | Buf8[i];
		}
		Buf16[i/2+1] = (uint16_t)Buf8[i]<<8;
	}
	else
	{
		for(i=0; i<len; i+=2)
		{
				Buf16[i/2] = ((uint16_t)Buf8[i+1]<<8) | Buf8[i];
		}
	}
	return re;
}

void U16toU8(char *Buf8,uint16_t *Buf16,int len)
{
	for(int i=0; i<len; i++)
	{
			Buf8[i] = Buf16[i];
	}
}

void U16separateU8(uint8_t *Buf8,uint16_t *Buf16,int len)
{
	int index=0,i;
	for(i=0; i<len; i++)
	{
		Buf8[index+1]   = (Buf16[i]>>8)&0x00ff;
		Buf8[index] = Buf16[i]&0x00ff;
		index+=2;
	}
}



